package com.deductive.common.config.mybatis;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.locationtech.jts.geom.*;

import java.io.IOException;

/**
 * Jackson serializer to convert JTS Geometry objects to GeoJSON format
 */
public class GeometryToGeoJsonSerializer extends JsonSerializer<Geometry> {

    @Override
    public void serialize(Geometry geometry, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (geometry == null) {
            gen.writeNull();
            return;
        }

        gen.writeStartObject();
        gen.writeStringField("type", geometry.getGeometryType());
        gen.writeFieldName("coordinates");
        serializeCoordinates(geometry, gen);
        gen.writeEndObject();
    }

    private void serializeCoordinates(Geometry geometry, JsonGenerator gen) throws IOException {
        if (geometry instanceof Point) {
            serializeCoordinate(((Point) geometry).getCoordinate(), gen);
        } else if (geometry instanceof LineString) {
            serializeLineString(((LineString) geometry), gen);
        } else if (geometry instanceof Polygon) {
            serializePolygon((Polygon) geometry, gen);
        } else if (geometry instanceof MultiPoint) {
            gen.writeStartArray();
            for (Coordinate coord : ((MultiPoint) geometry).getCoordinates()) {
                serializeCoordinate(coord, gen);
            }
            gen.writeEndArray();
        } else if (geometry instanceof MultiLineString) {
            MultiLineString multiLineString = (MultiLineString) geometry;
            gen.writeStartArray();
            for (int i = 0; i < multiLineString.getNumGeometries(); i++) {
                serializeLineString((LineString) multiLineString.getGeometryN(i), gen);
            }
            gen.writeEndArray();
        } else if (geometry instanceof MultiPolygon) {
            MultiPolygon multiPolygon = (MultiPolygon) geometry;
            gen.writeStartArray();
            for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
                serializePolygon((Polygon) multiPolygon.getGeometryN(i), gen);
            }
            gen.writeEndArray();
        } else {
            gen.writeString("Unsupported geometry type: " + geometry.getGeometryType());
        }
    }

    private void serializeCoordinate(Coordinate coordinate, JsonGenerator gen) throws IOException {
        gen.writeStartArray();
        gen.writeNumber(coordinate.getX());
        gen.writeNumber(coordinate.getY());
        if (!Double.isNaN(coordinate.getZ())) {
            gen.writeNumber(coordinate.getZ());
        }
        gen.writeEndArray();
    }

    private void serializeLineString(LineString lineString, JsonGenerator gen) throws IOException {
        gen.writeStartArray();
        for (Coordinate coord : lineString.getCoordinates()) {
            serializeCoordinate(coord, gen);
        }
        gen.writeEndArray();
    }

    private void serializePolygon(Polygon polygon, JsonGenerator gen) throws IOException {
        gen.writeStartArray(); // Start of polygon rings array

        // Exterior ring
        serializeLineString(polygon.getExteriorRing(), gen);

        // Interior rings
        for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
            serializeLineString(polygon.getInteriorRingN(i), gen);
        }

        gen.writeEndArray(); // End of polygon rings array
    }
}
